
package com.huaweicloud.saaslanding.install.applicationservice.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.huaweicloud.saaslanding.basic.enumeration.ComponentLabelEnum;
import com.huaweicloud.saaslanding.basic.enumeration.ServiceManageFrameTypeEnum;
import com.huaweicloud.saaslanding.basic.utils.EncryptionUtil;
import com.huaweicloud.saaslanding.basic.utils.StringFormatConversion;
import com.huaweicloud.saaslanding.basic.utils.ThreadPoolManager;
import com.huaweicloud.saaslanding.dao.common.entity.app.AppDeployment;
import com.huaweicloud.saaslanding.dao.common.entity.app.AppTemplate;
import com.huaweicloud.saaslanding.dao.common.entity.cloudservice.CseInfo;
import com.huaweicloud.saaslanding.dao.common.entity.cloudservice.RdsResource;
import com.huaweicloud.saaslanding.dao.common.entity.security.CertStoreInfo;
import com.huaweicloud.saaslanding.dao.common.repository.app.AppDeploymentBaseMapper;
import com.huaweicloud.saaslanding.dao.common.repository.app.AppTemplateBaseMapper;
import com.huaweicloud.saaslanding.dao.common.repository.cloudservice.CseInfoBaseMapper;
import com.huaweicloud.saaslanding.dao.common.repository.cloudservice.RdsResourceBaseMapper;
import com.huaweicloud.saaslanding.dao.common.repository.security.CertStoreInfoBaseMapper;
import com.huaweicloud.saaslanding.dao.common.repository.tenant.TenantBaseMapper;
import com.huaweicloud.saaslanding.install.applicationservice.ServiceManageFrameService;
import com.huaweicloud.saaslanding.install.dto.CseDto;
import com.huaweicloud.saaslanding.install.dto.SaltDto;
import com.huaweicloud.saaslanding.install.dto.ServiceManageFrameworkDto;
import com.huaweicloud.saaslanding.install.properties.Nacos;
import com.huaweicloud.saaslanding.install.properties.Ssl;
import com.huaweicloud.saaslanding.install.repository.ComponentInfoMapper;
import com.huaweicloud.saaslanding.install.repository.WorkSecretKeyMapper;
import com.huaweicloud.saaslanding.install.util.nacos.NacosNamespaceUtil;

import cn.hutool.core.util.StrUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Value;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 微服务管理平台基类
 *
 * @since 2022-11-01
 */
@Slf4j
public abstract class AbstractServiceManageFrameServiceImpl implements ServiceManageFrameService {
    private static final String PREDICATE_PATH = "/**";

    /**
     * 初始化线程池
     */
    ThreadPoolExecutor poolExecutor = ThreadPoolManager.newInstance();

    @Resource
    private AppTemplateBaseMapper appTemplateBaseMapper;

    @Resource
    private TenantBaseMapper tenantBaseMapper;

    @Resource
    private ComponentInfoMapper componentInfoMapper;

    @Resource
    private WorkSecretKeyMapper workSecretKeyMapper;

    @Resource
    private AppDeploymentBaseMapper appDeploymentBaseMapper;

    @Resource
    private RdsResourceBaseMapper rdsResourceBaseMapper;

    @Resource
    private CertStoreInfoBaseMapper certStoreInfoBaseMapper;

    @Resource
    private CseInfoBaseMapper cseInfoBaseMapper;

    @Resource
    private Ssl ssl;

    @Value("${saaslanding-install.config-discovery-frame.type}")
    private String CONFIG_DISCOVERY_FRAME;

    @Value("${spring.profiles.active}")
    private String ACTIVE;

    @Value("${saaslanding-install.skywalking.uiReportHost}")
    private String skyWalkingUiReportHost;

    @Value("${saaslanding-install.ingress.ingressClassName}")
    private String ingressClassName;

    @Resource
    private Nacos nacos;

    @Value("${saaslanding-install.waf.enabled:false}")
    private Boolean wafEnabeld;

    @Value("${saaslanding-install.cdn.enabled:false}")
    private Boolean cdnEnabeld;

    @Override
    public List<ServiceManageFrameworkDto> createServiceManageFrameConfigInfoDtoList() {
        Long appId = appTemplateBaseMapper.selectIdByType(1);

        String tenantCode = tenantBaseMapper.getIsvTenantCode();

        AppTemplate appTemplate = appTemplateBaseMapper.selectById(appId);

        AppDeployment appDeployment = appDeploymentBaseMapper.getAppDeploymentByAppId(appId);

        // 数据库信息
        RdsResource rdsResource = rdsResourceBaseMapper.selectById(appDeployment.getRdsId());
        String rdsPassword = "ENC(" + rdsResource.getDbPassword() + ")";

        // druid信息
        SaltDto saltDto = workSecretKeyMapper.getPlatformSaltDto();
        String salt = EncryptionUtil.decrypt(saltDto.getPwdSalt(), saltDto.getPassword());
        String druidPassword = "ENC(" + EncryptionUtil.encrypt(salt, "saaslanding") + ")";

        // cse或nacos信息
        String namespaceId = StrUtil.EMPTY;
        if (ServiceManageFrameTypeEnum.NACOS.getName().equals(CONFIG_DISCOVERY_FRAME)) {
            QueryWrapper<CseInfo> qw = new QueryWrapper<>();
            qw.lambda().eq(CseInfo::getType, ServiceManageFrameTypeEnum.NACOS.getName()).eq(CseInfo::getIsDel, 0);
            CseInfo cseInfo = cseInfoBaseMapper.selectOne(qw);
            namespaceId = cseInfo.getNamespaceId();
        }

        // ssl信息
        QueryWrapper<CertStoreInfo> storeInfoQueryWrapper = new QueryWrapper<>();
        storeInfoQueryWrapper.eq("is_del", 0);
        CertStoreInfo hslCertStoreInfo = certStoreInfoBaseMapper.selectOne(storeInfoQueryWrapper);

        String keyStorePwd = hslCertStoreInfo != null ? "ENC(" + hslCertStoreInfo.getKeyStorePwd() + ")" : "";
        String trustStorePwd = hslCertStoreInfo != null ? "ENC(" + hslCertStoreInfo.getTrustStorePwd() + ")" : "";

        // 组件基础信息
        List<CseDto> cseDtoList = componentInfoMapper.getCseDto(appId);
        List<ServiceManageFrameworkDto> serviceManageFrameworkDtoList = new ArrayList<>();

        for (CseDto cseDto : cseDtoList) {
            ServiceManageFrameworkDto dto = new ServiceManageFrameworkDto();

            if (ComponentLabelEnum.GATEWAY.getLabel().equals(cseDto.getLabel())) {
                initGatewayData(cseDtoList, dto);
            }

            dto.setAppName(tenantCode)
                .setNamespaceId(namespaceId)
                .setServiceName(cseDto.getName())
                .setLabel(cseDto.getLabel())
                .setComponentId(cseDto.getComponentId())
                .setServerPort(cseDto.getPort())
                .setServletPath(cseDto.getContextPath())
                .setDomain(appTemplate.getDomain())
                .setRdsIp(rdsResource.getDbIp())
                .setRdsPort(Integer.parseInt(rdsResource.getDbPort()))
                .setRdsDatabase(tenantCode)
                .setRdsUserName(rdsResource.getDbUsername())
                .setRdsPassword(rdsPassword)
                .setDruidPassword(druidPassword)
                .setSkyWalkingUiReportHost(skyWalkingUiReportHost)
                .setKeyStorePwd(keyStorePwd)
                .setTrustStorePwd(trustStorePwd)
                .setKeyStorePath(hslCertStoreInfo != null ? hslCertStoreInfo.getKeyStorePath() : "")
                .setTrustStorePath(hslCertStoreInfo != null ? hslCertStoreInfo.getTrustStorePath() : "")
                .setSslEnable(ssl.isEnabled())
                .setActive(ACTIVE)
                .setIngressClassName(ingressClassName)
                .setWafEnabled(wafEnabeld)
                .setCdnEnabled(cdnEnabeld);

            serviceManageFrameworkDtoList.add(dto);
        }
        return serviceManageFrameworkDtoList;
    }

    @Override
    public void createNacosNamespace() {
        if (CONFIG_DISCOVERY_FRAME.equals(ServiceManageFrameTypeEnum.NACOS.getName())) {
            NacosNamespaceUtil.get(nacos.getNacosServers().get(0), nacos.getNamespace());
        }
    }

    @Override
    public void initServiceManageFramework() {
        {
            // 获取appName
            log.info("<-----------------初始化管理平台配置文件中----------------->");
            // nacos 创建namespace
            createNacosNamespace();
            // 获取serviceName列表
            List<ServiceManageFrameworkDto> serviceManageFrameConfigInfoDtoList =
                createServiceManageFrameConfigInfoDtoList();
            // stream->多线程执行cse配置创建
            CompletableFuture<?>[] cfs = serviceManageFrameConfigInfoDtoList.stream()
                .map(object -> CompletableFuture.runAsync(() -> createServiceManageFrameConfig(object), poolExecutor)
                    .thenAccept(v -> log.info("-----------------" + object.getServiceName() + "配置文件创建成功!----"))
                    .exceptionally(e -> {
                        // 截取错误信息
                        String message = object.getServiceName() + "初始化失败..."
                            + StringFormatConversion.cutString(e.getMessage(), "=", ",");
                        log.error(message);
                        throw new CompletionException(e);
                    }))
                .toArray(CompletableFuture[]::new);

            // 等待总任务完成，但是无返回值，必须whenComplete()获取
            CompletableFuture.allOf(cfs).join();
            log.info("<-----------------初始化管理平台配置文件完成----------------->");
        }
    }

    /**
     * 初始化网关配置所需数据
     *
     * @param cseDtoList
     * @param dto
     */
    private void initGatewayData(List<CseDto> cseDtoList, ServiceManageFrameworkDto dto) {
        for (CseDto cseDto : cseDtoList) {
            if (ComponentLabelEnum.TENANT.getLabel().equals(cseDto.getLabel())) {
                dto.setTenantName(cseDto.getName()).setTenantPath(cseDto.getContextPath() + PREDICATE_PATH);
            } else if (ComponentLabelEnum.AUTHORITY.getLabel().equals(cseDto.getLabel())) {
                dto.setAuthorityName(cseDto.getName()).setAuthorityPath(cseDto.getContextPath() + PREDICATE_PATH);
            } else if (ComponentLabelEnum.DEPLOYMENT.getLabel().equals(cseDto.getLabel())) {
                dto.setDeploymentName(cseDto.getName()).setDeploymentPath(cseDto.getContextPath() + PREDICATE_PATH);
            } else if (ComponentLabelEnum.UPGRADE.getLabel().equals(cseDto.getLabel())) {
                dto.setUpgradeName(cseDto.getName()).setUpgradePath(cseDto.getContextPath() + PREDICATE_PATH);
            } else if (ComponentLabelEnum.INTEGRATED.getLabel().equals(cseDto.getLabel())) {
                dto.setIntegratedName(cseDto.getName()).setIntegratedPath(cseDto.getContextPath() + PREDICATE_PATH);
            } else if (ComponentLabelEnum.MAINTENANCE.getLabel().equals(cseDto.getLabel())) {
                dto.setMaintenanceName(cseDto.getName()).setMaintenancePath(cseDto.getContextPath() + PREDICATE_PATH);
            } else if (ComponentLabelEnum.AUTH.getLabel().equals(cseDto.getLabel())) {
                dto.setAuthName(cseDto.getName()).setAuthPath(cseDto.getContextPath() + PREDICATE_PATH);
            } else if (ComponentLabelEnum.LOG.getLabel().equals(cseDto.getLabel())) {
                dto.setLogName(cseDto.getName()).setLogPath(cseDto.getContextPath() + PREDICATE_PATH);
            } else if (ComponentLabelEnum.MEASUREMENT.getLabel().equals(cseDto.getLabel())) {
                dto.setMeasurementName(cseDto.getName()).setMeasurementPath(cseDto.getContextPath() + PREDICATE_PATH);
            } else if (ComponentLabelEnum.OPERATION.getLabel().equals(cseDto.getLabel())) {
                dto.setOperationName(cseDto.getName()).setOperationPath(cseDto.getContextPath() + PREDICATE_PATH);
            } else if (ComponentLabelEnum.JOB.getLabel().equals(cseDto.getLabel())) {
                dto.setJobName(cseDto.getName()).setJobPath(cseDto.getContextPath() + PREDICATE_PATH);
            }
        }
    }

}
